home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Scene 96
/
Scene 96 International Edition (Zyklop Software) (Disc 2) (1997).iso
/
misc
/
coding
/
vgacodng
/
part08.txt
< prev
next >
Wrap
Text File
|
1996-12-15
|
13KB
|
351 lines
VGA-Kurs - Part #8
Hallo! Leider mußte 'T.C.P.'s Beginner's Guide To VGA Coding' in der letzten
Ausgabe ausfallen, weil ich keine Zeit hatte, aber diesmal gibt es wieder einen
neuen Teil, und zwar den 8.!
Und der behandelt auf vielfachen Wunsch (von 2 Lesern) das Thema 'Plasmas'.
Erstmal eine kleine Erklärung für die, die noch nie solch einem Effekt begegnet
sind: Es gibt zwei Arten von Plasmas. Das Colorcycle- und das Realtime- oder
auch Sinus-Plasma. Beim Colorcycle-Plasma handelt es sich um ein nach einer
bestimmten Formel vorberechnetes, buntes Bild, dessen Farben einfach per
Palettenrotation vertauscht werden. Um das Bild zu berechnen bedient man sich
der Iteration. Es werden also 9 Punkte (8 am Rand, einer in der Mitte)
zufälliger Farbe auf den Screen gesetzt, so daß sie ihn in 4 Rechtecke teilen.
Nun verfährt man mit diesen Rechtecken genauso wie mit dem Screen am Anfang,
nur daß die neuen Punkte aus den alten durch Interpolation errechnet werden.
Da diese Art von Plasma aber sowieso die uninteressantere ist, hier nur
schnell der Source und dann ohne große Erklärungen zum Sinus-Plasma.
uses crt;
var n : byte;
Palette : array[0..767] of byte;
function newcol(mc,n,dvd:integer) : byte;
begin
newcol := ((mc+n-random(n)) div dvd) mod 192;
end;
procedure subdivide(x1,y1,x2,y2:word);
var x,y,dxy,n1,n2,n3,n4 : word;
begin
if (x2-x1 < 2) and (y2-y1 < 2) then exit;
x := (x2+x1) div 2;
y := (y2+y1) div 2;
n1 := mem[$A000:320*y1+x1];
n2 := mem[$A000:320*y2+x1];
n3 := mem[$A000:320*y1+x2];
n4 := mem[$A000:320*y2+x2];
dxy := 5 * (x2-x1+y2-y1) div 3;
if mem[$A000:320*y1+x] = 0 then mem[$A000:320*y1+x] := newcol(n1+n3,dxy,2);
if mem[$A000:320*y+x1] = 0 then mem[$A000:320*y+x1] := newcol(n1+n2,dxy,2);
if mem[$A000:320*y+x2] = 0 then mem[$A000:320*y+x2] := newcol(n3+n4,dxy,2);
if mem[$A000:320*y2+x] = 0 then mem[$A000:320*y2+x] := newcol(n2+n4,dxy,2);
mem[$A000:320*y+x] := newcol(n1+n2+n3+n4,dxy,4);
subdivide(x1,y1,x,y);
subdivide(x,y1,x2,y);
subdivide(x1,y,x,y2);
subdivide(x,y,x2,y2);
end;
procedure SetPal(col,r,g,b:byte);
begin
port[$3C8] := col;
port[$3C9] := r;
port[$3C9] := g;
port[$3C9] := b;
end;
procedure RotatePalette;
var Temp : array[0..2] of byte;
begin
repeat
move(Palette,Temp,3);
move(Palette[3],Palette[0],765);
move(Temp,Palette[765],3);
for n := 0 to 255 do setpal(n,Palette[n*3],Palette[n*3+1],Palette[n*3+2]);
until keypressed;
end;
begin
randomize;
asm mov ax,13h; int 10h end; { In Mode 13h schalten }
fillchar(Palette,768,0); { Palette erstellen }
for n := 0 to 255 do begin
Palette[n*3+1] := n div 2;
Palette[n*3+2] := n;
setpal(n,0,n div 2,n);
end;
subdivide(0,0,319,199); { Plasma aufbauen }
RotatePalette; { Palette rotieren }
readkey;
asm mov ax,3; int 10h end; { Zurück zum Textmodus }
end.
Der Grund, warum dieses Plasma eher langweilig wirkt, ist das Fehlen einer
Bewegung. Außerdem wären etwas rundere Formen angenehm.
Diese Mängel behebt das Sinus-Plasma. Sein Nachteil ist allerdings, daß es
sehr viel mehr Rechenzeit erfordert, deshalb sollte es möglichst in Assembler
geschrieben werden.
Doch wie macht man das überhaupt? Zuerst benötigt man eine (Co)Sinus-Tabelle.
Diese entscheidet nachher die Form des Plasmas, allerdings nicht allein.
Auch wie die Werte aus dieser Tabelle entnommen werden, ist relevant.
Als Beispiel benutzen wir hier die Formel
Farbwert := SinTab[Wert1] + SinTab[Wert2] + SinTab[Wert3] + SinTab[Wert4]
Die vier Indizes sind Werte, die bei jedem Durchlauf um einen bestimmten oder
zufälligen Wert erhöht werden, wodurch eine Bewegung zustande kommt.
Da wir die Variablen innerhalb der Loops verändern, müssen wir sie vorher in
Hilfsvariablen (TWert1-4) sichern.
Die Variablen Wert1 und Wert2 sind für das horizontale und Wert3 und Wert4 für
das vertikale Muster des Plasmas verantwortlich. Sie werden in den
entsprechenden Loops um feste Werte erhöht, die die Größe der Plasma-Kreise
bestimmen.
Das Ganze nun erstmal in einer 100%-Pascal-Version:
uses crt;
var Wert1,Wert2,Wert3,Wert4 : byte;
TWert1,TWert2,TWert3,TWert4 : byte;
SinTab : array[0..255] of byte;
n1,n2 : word;
col : byte;
procedure SetPal(col,r,g,b:byte);
begin
port[$3C8] := col;
port[$3C9] := r;
port[$3C9] := g;
port[$3C9] := b;
end;
procedure CalcSinus(Ofs,Amp:byte;Len,Par:word);
begin
for n1 := 0 to Len do SinTab[n1] := round(sin(n1/Par*pi*Len/180*2)*Amp)+Ofs;
end;
begin
CalcSinus(32,31,255,360);
asm mov ax,13h; int 10h end;
for n1 := 0 to 127 do begin
setpal(n1,n1 div 6,n1 div 3,n1 div 3);
setpal(255-n1,n1 div 6,n1 div 3,n1 div 3);
end;
repeat
TWert3 := Wert3;
TWert4 := Wert4;
for n1 := 0 to 319 do begin
TWert1 := Wert1;
TWert2 := Wert2;
for n2 := 0 to 199 do begin
col := SinTab[TWert1] + SinTab[TWert2] +
SinTab[TWert3] + SinTab[TWert4]; { Farbwert berechnen }
mem[$A000:n2*320+n1] := col;
inc(TWert1,4);
inc(TWert2,3);
end;
inc(TWert3,4);
inc(TWert4,5);
end;
dec(Wert1,4); { Bewegung des Plasmas }
inc(Wert3,4);
inc(Wert1,random(1)); { Zufallswerte bewirken, daß das Plasma }
dec(Wert2,random(2)); { etwas unregelmäßig wird }
inc(Wert3,random(1));
dec(Wert4,random(2));
until keypressed;
readkey;
asm mov ax,3; int 10h end;
end.
Wer nicht gerade einen schnellen 486 hat, wird wohl mit der Geschwindigkeit
nicht ganz zufrieden sein, deshalb jetzt nochmal die Repeat-Schleife in der
Assembler-Version:
{ Einfach die Repeat-Schleife aus dem oberen Listing löschen und diese
Zeilen einfügen: }
asm
mov ax,0A000h { VGA-Segment nach ES }
mov es,ax
@mainloop:
xor di,di { Bildschirmoffset auf 0 }
mov al,Wert3 { Werte sichern }
mov TWert3,al
mov al,Wert4
mov TWert4,al
mov n1,0 { Zähler initialisieren }
@loop1:
mov al,Wert1 { Werte sichern }
mov TWert1,al
mov al,Wert2
mov TWert2,al
mov n2,0 { Zähler initialisieren }
@loop2:
xor bx,bx { Farbwert wird in BX berechnet }
mov al,TWert4 { Index holen }
xor ah,ah
mov si,ax
mov al,[si+offset SinTab] { Wert auslesen }
add bx,ax { und auf BX addieren }
mov al,TWert3
mov si,ax
mov al,[si+offset SinTab]
add bx,ax
mov al,TWert2
mov si,ax
mov al,[si+offset SinTab]
add bx,ax
mov al,TWert1
mov si,ax
mov al,[si+offset SinTab]
add bx,ax
mov es:[di],bl { Pixel setzen }
inc di { Bildschirmoffset erhöhen }
add TWert1,4 { Indizes erhöhen }
add TWert2,3
inc n2
cmp n2,320
jne @loop2
add TWert3,4
add TWert4,5
inc n1
cmp n1,200
jne @loop1
sub Wert1,4
add Wert3,4
mov ah,0Bh
int 21h
or al,al
jz @mainloop
end;
Man kann nun das Aussehen des Plasmas verändern, indem man z.B. das Offset und
die Amplitude der Sinustabelle beim Aufruf von CalcSinus ändert.
Außerdem könnte man hier parallel noch eine Palettenrotation einbauen, das
kostet nicht viel Rechenzeit, sieht aber um so besser aus.
Dieses Beispiel ist natürlich nur eine von vielen Methoden, Sinus-Plasmas zu
coden. Es wurden schon einige hier im PCH abgedruckt, wenn ihr sie noch habt,
seht sie euch doch nochmal an, ihr erfahrt dort bestimmt noch einiges mehr.
(Hey, an dieser Stelle hat der VGA-Kurs das 100.000ste Byte erreicht!!!)
Dieser Teil des Kurses ist noch nicht zu Ende! Es folgt nun ein Bonusteil,
und zwar über Fraktale, wie Onkel Joe es sich im PCH 3/96 gewünscht hat.
(Ich hätte nichts gegen weitere Bonusteile, wenn euch also ein Randthema der
VGA-Programmierung interessiert, schreibt mir, und ich werde mal sehen, ob ich
dazu was schreiben kann.)
Jeder kennt sie, jeder ist fasziniert von ihnen: Fraktale. Doch wie werden
diese Dinger eigentlich berechnet?
Das funktioniert so ähnlich wie bei der Berechnung der Colorcycle-Plasmas,
allerdings kommt man hier nicht um Real-Zahlen herum, was sich nicht unerheblich
auf die Berechnungsgeschwindigkeit auswirkt.
Wie beim Plasma (das übrigens auch ein Fraktal ist) wird mit Iteration
gearbeitet, d.h. daß die Werte, die im letzen Schleifendurchlauf berechnet
wurden, als Basis für die neuen Berechnungen herangezogen werden.
Der folgende Source behandelt die beiden populärsten Fraktal-Typen, Mandelbrot
und Julia. Für die beiden Typen ist jeweils eine Prozedur zuständig, die aus
den X- und Y-Koordinaten des Pixels den zugehörigen Farbwert des Fraktals
berechnet. Ihr könnt die Berechnungsweise aus den Prozeduren entnehmen, die
Erklärung dieser Formeln liegt leider außerhalb meines mathematischen
Wissensstandes.
{$N+,E+}
uses crt;
{ Diese Konstanten können verändert werden, um das Aussehen der Fraktale zu
bestimmen. }
const Colors = 32; { Anzahl der Farben des Fraktals }
Width = 320; { Breite des Fraktals }
Height = 200; { Höhe des Fraktals }
Limit = 8.0; { Berechnungsgrenze, regelt Schärfe }
XRMin = -2.0; { Linke Grenze des Fraktals }
XRMax = 1.0; { Rechte Grenze des Fraktals }
YRMin = -1.3; { Obere Grenze des Fraktals }
YRMax = 1.3; { Untere Grenze des Fraktals }
type real = double; { Zur Optimierung der Fließkomma-Berechnungen }
var XPos,YPos : word;
RealP,ImagP : real;
CurrX,CurrY : real;
a2,b2 : real;
n : byte;
function CalcColorMandel(XPos,YPos:word) : byte;
begin
CurrX := XPos / Width * (XRMax-XRMin) + XRMin;
CurrY := YPos / Height * (YRMax-YRMin) + YRMin;
RealP := 0;
ImagP := 0;
n := 0;
repeat
a2 := sqr(RealP);
b2 := sqr(ImagP);
ImagP := 2 * RealP * ImagP + CurrY;
RealP := a2 - b2 + CurrX;
inc(n);
until (n >= Colors) or (a2+b2 >= Limit);
CalcColorMandel := n - 1;
end;
function CalcColorJulia(XPos,YPos:word) : byte;
begin
CurrX := 0.07;
CurrY := -0.4;
RealP := -2 + 0.5 + XPos / (Width/3);
ImagP := 2 - 0.5 - YPos / (Height/3);
n := 0;
repeat
a2 := sqr(ImagP) - sqr(RealP) + CurrX;
b2 := 2 * RealP * ImagP + CurrY;
RealP := a2;
ImagP := b2;
inc(n);
until (n >= Colors) or (sqr(a2)+sqr(b2) > 4) or
(abs(a2) > 2) or (abs(b2) > 2);
CalcColorJulia := n - 1;
end;
begin
asm mov ax,13h; int 10h end; { In Mode 13h schalten }
for YPos := 0 to Height-1 do { Mandelbrot-Menge aufbauen }
for XPos := 0 to Width-1 do
mem[$A000:YPos*320+XPos] := CalcColorMandel(XPos,YPos);
readkey;
for YPos := 0 to Height-1 do { Julia-Menge aufbauen }
for XPos := 0 to Width-1 do
mem[$A000:YPos*320+XPos] := CalcColorJulia(XPos,YPos);
readkey;
asm mov ax,3; int 10h end; { Zurück zum Textmodus }
end.
So, wir sind mal wieder am Ende angelangt, und es folgt die Vorschau auf den
nächsten Teil: Ich will nicht zu viel versprechen, aber ich werde beim nächsten
mal mit 3D-Grafik beginnen. Dieses Thema ist wirklich nicht leicht anschaulich
zu machen, vor allem Leser ohne Mathekenntnisse (Geometrie!) werden einige
Probleme haben, aber ich werde es trotzdem versuchen. Also, bis bald!
[ This text copyright (c) 1995-96 Johannes Spohr. All rights reserved. ]
[ ]
[ No part of this document may be reproduced, transmitted, ]
[ transcribed, stored in a retrieval system, or translated into any ]
[ human or computer language, in any form or by any means; electronic, ]
[ mechanical, magnetic, optical, chemical, manual or otherwise, ]
[ without the expressed written permission of the author. ]
[ ]
[ The information contained in this text is believed to be correct. ]
[ The text is subject to change without notice and does not represent ]
[ a commitment on the part of the author. ]
[ The author does not make a warranty of any kind with regard to this ]
[ material, including, but not limited to, the implied warranties of ]
[ merchantability and fitness for a particular purpose. The author ]
[ shall not be liable for errors contained herein or for incidental or ]
[ consequential damages in connection with the furnishing, performance ]
[ or use of this material. ]